unit uExpressionEvaluationTree;

interface

uses uKalkulesMathDefines, uNumber, uExpressionTree;

type

  TExpressionEvaluationTreeClass = class of TExpressionEvaluationTree;

  TExpressionEvaluationTree = class(TExpressionTree)
  private
    FAngleMode: TAngleMode;

    procedure EvaluateNumber(AResult: TNumber);
    procedure EvaluateAddition(AResult: TNumber);
    procedure EvaluateSubtraction(AResult: TNumber);
    procedure EvaluateMultiplication(AResult: TNumber);
    procedure EvaluateDivision(AResult: TNumber);
    procedure EvaluateFactorial(AResult: TNumber);
    procedure EvaluatePower(AResult: TNumber);
    procedure EvaluateFunction(AResult: TNumber);
    procedure EvaluatePercentage(AResult: TNumber);

    function GetLeftSubtree: TExpressionEvaluationTree;
    function GetRightSubtree: TExpressionEvaluationTree;

  public
    constructor Create(ASubtreeClass: TExpressionEvaluationTreeClass);

    procedure Evaluate(AResult: TNumber);

    property AngleMode: TAngleMode read FAngleMode write FAngleMode;
  end;

implementation

uses uNumberExceptions, uNumberFunction, uNumberFunctionFactory,
  uNumberWithFunctions;

{ TExpressionEvaluationTree }

constructor TExpressionEvaluationTree.Create(ASubtreeClass: TExpressionEvaluationTreeClass);
begin
  inherited Create(ASubtreeClass);
  FAngleMode := amDeg;
end;

procedure TExpressionEvaluationTree.Evaluate(AResult: TNumber);
begin
  case Self.ExpressionType of
    etNumber:
      EvaluateNumber(AResult);
    etAddition:
      EvaluateAddition(AResult);
    etSubtraction:
      EvaluateSubtraction(AResult);
    etMultiplication:
      EvaluateMultiplication(AResult);
    etDivision:
      EvaluateDivision(AResult);
    etFactorial:
      EvaluateFactorial(AResult);
    etPower:
      EvaluatePower(AResult);
    etFunction:
      EvaluateFunction(AResult);
    etPercentage:
      EvaluatePercentage(AResult);
  end;
end;

procedure TExpressionEvaluationTree.EvaluateAddition(AResult: TNumber);
var
  rightSubresult: TNumber;
begin
  rightSubresult := AResult.CreateNumberOfSameType;
  try
    GetLeftSubtree.Evaluate(AResult);
    GetRightSubtree.Evaluate(rightSubresult);
    AResult.Add(rightSubresult);
  finally
    rightSubresult.Destroy;
  end;
end;

procedure TExpressionEvaluationTree.EvaluateDivision(AResult: TNumber);
var
  rightSubresult: TNumber;
begin
  rightSubresult := AResult.CreateNumberOfSameType;
  try
    GetLeftSubtree.Evaluate(AResult);
    GetRightSubtree.Evaluate(rightSubresult);
    AResult.DivideBy(rightSubresult);
  finally
    rightSubresult.Destroy;
  end;
end;

procedure TExpressionEvaluationTree.EvaluateFactorial(AResult: TNumber);
begin
  GetLeftSubtree.Evaluate(AResult);
  AResult.MakeFactorial;
end;

procedure TExpressionEvaluationTree.EvaluateFunction(AResult: TNumber);
var
  numberFunction: TNumberFunction;
begin
  if not (AResult is TNumberWithFunctions) then
  begin
    raise EUnsupportedFeature.CreateFmt('Numbers of type %s do not support functions.',[AResult.ClassName]);
  end;

  numberFunction := TNumberFunctionFactory.GetInstance.CreateFunction(Self.StringValue);
  if numberFunction = nil then
  begin
    raise EUnsupportedFeature.CreateFmt('Unknown function: %s',[Self.StringValue]);
  end;

  try
    GetRightSubtree.Evaluate(AResult);
    numberFunction.AngleMode := FAngleMode;
    TNumberWithFunctions(AResult).ApplyFunction(numberFunction);
  finally
    numberFunction.Destroy;
  end;
end;

procedure TExpressionEvaluationTree.EvaluateMultiplication(AResult: TNumber);
var
  rightSubresult: TNumber;
begin
  rightSubresult := AResult.CreateNumberOfSameType;
  try
    GetLeftSubtree.Evaluate(AResult);
    GetRightSubtree.Evaluate(rightSubresult);
    AResult.MultiplyBy(rightSubresult);
  finally
    rightSubresult.Destroy;
  end;
end;

procedure TExpressionEvaluationTree.EvaluateNumber(AResult: TNumber);
begin
  AResult.FromString(Self.StringValue);
end;

procedure TExpressionEvaluationTree.EvaluatePower(AResult: TNumber);
var
  rightSubresult: TNumber;
begin
  rightSubresult := AResult.CreateNumberOfSameType;
  try
    GetLeftSubtree.Evaluate(AResult);
    GetRightSubtree.Evaluate(rightSubresult);
    AResult.Power(rightSubresult);
  finally
    rightSubresult.Destroy;
  end;
end;

procedure TExpressionEvaluationTree.EvaluateSubtraction(AResult: TNumber);
var
  rightSubresult: TNumber;
begin
  rightSubresult := AResult.CreateNumberOfSameType;
  try
    GetLeftSubtree.Evaluate(AResult);
    GetRightSubtree.Evaluate(rightSubresult);
    AResult.Subtract(rightSubresult);
  finally
    rightSubresult.Destroy;
  end;
end;

procedure TExpressionEvaluationTree.EvaluatePercentage(AResult: TNumber);
begin
  //TODO: implement EvaluatePercentage
end;

function TExpressionEvaluationTree.GetLeftSubtree: TExpressionEvaluationTree;
begin
  Result := Self.LeftSubtree as TExpressionEvaluationTree;
end;

function TExpressionEvaluationTree.GetRightSubtree: TExpressionEvaluationTree;
begin
  Result := Self.RightSubtree as TExpressionEvaluationTree;
end;

end.
